﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using DSS_XNA.Engine.Error;
using DSS_XNA;
using DSS_XNA.Engine.Script.Statements;
using DSS_XNA.Engine.Script.Variables;

namespace DSS_XNA.Engine.Script
{
    public class Script
    {
        #region Properties

        public List<string> ScriptLines
        {
            get { return _scriptLines; }
            set { _scriptLines = value; }
        }
        private List<string> _scriptLines = new List<string>();

        public List<ScriptCommand> Commands
        {
            get { return _commands; }
            set { _commands = value; }
        }
        private List<ScriptCommand> _commands = new List<ScriptCommand>();

        public List<ScriptCommand> ExtraCommands
        {
            get { return _extraCommands; }
            set { _extraCommands = value; }
        }
        private List<ScriptCommand> _extraCommands = new List<ScriptCommand>();

        public ScriptManager ScriptManager
        {
            get { return _scriptManager; }
            set { _scriptManager = value; }
        }
        private ScriptManager _scriptManager = null;

        public Dictionary<string, IVariable> Variables
        {
            get { return _variables; }
            set { _variables = value; }
        }
        private Dictionary<string, IVariable> _variables = new Dictionary<string, IVariable>();

        #endregion

        #region Public Methods

        public Script(ScriptManager manager)
        {
            if (manager == null)
                throw new DSSException("'manager' is null.");

            this.ScriptManager = manager;
        }

        public bool Load()
        {
            if (this.ScriptLines.Count == 0)
                throw new DSSException("No script lines to load");

            for(int i = 0; i < this.ScriptLines.Count; i++)
            {
                string line = this.ScriptLines[i].Trim();

                // is this line a comment?
                if (line.StartsWith("//"))
                    continue;

                // is this an if statment
                if (line.StartsWith("if"))
                {
                    // have a command contain an if statement and it will run as an if statement if it has one - that way the runonce modifiers etc... will be able to work with it
                    If ifStatement = new If(this.ScriptManager);
                    ifStatement.ReadInStatement(line);

                    // read in up to the end if
                    if(i >= this.ScriptLines.Count)
                        throw new DSSException("End of file reached before end of IF statement");

                    bool currentClause = true; // are we reading in for the true or false part of the if statement?
                    for(i = i + 1; i < this.ScriptLines.Count; i++) // we want to continue the reading here
                    {
                        line = this.ScriptLines[i].ToLower().Trim();
                        if (line == "endif")
                            break;

                        if (line.ToLower().Trim() == "else")
                        {
                            currentClause = false;
                            continue;
                        }

                        ifStatement.AddScriptCommand(line, currentClause);
                    }

                    ifStatement.Load();

                    ScriptCommand ifCommand = new ScriptCommand();
                    ifCommand.Load(ifStatement);
                    this.Commands.Add(ifCommand);

                    continue;
                }

                ScriptCommand command = new ScriptCommand();
                command.Load(line);
                this.Commands.Add(command);
            }

            return true;
        }

        public bool Load(string filePath)
        {
            FileStream fp = File.Open(filePath, FileMode.Open);
            StreamReader reader = new StreamReader(fp);

            string line = String.Empty;
            while(!reader.EndOfStream)
            {
                line = reader.ReadLine();
                if (String.IsNullOrEmpty(line))
                    continue;

                this.ScriptLines.Add(line);
            }

            reader.Close();

            return this.Load();
        }

        public void Run(Game1 engine)
        {
            if (engine == null)
                throw new DSSException("'engine' is null.");

            foreach (ScriptCommand command in this.Commands)
            {
                command.Run(this.ScriptManager, engine);
            }

            // do we have any extra commands to run?
            foreach (ScriptCommand command in this.ExtraCommands)
            {
                command.Run(this.ScriptManager, engine);
            }
            this.ExtraCommands.Clear();
        }

        public void AddVariable(IVariable var)
        {
            if (var == null)
                throw new DSSException("'var' is null.");

            if (this.Variables.ContainsKey(var.Name))
                throw new DSSException(String.Format("There is already a local variable with the name '{0}'",
                    var.Name));

            this.Variables.Add(var.Name, var);
        }

        #endregion
    }
}
